require 'depac-deployment/environment_configuration'
require 'depac-deployment/helper'
require 'depac-common/constants'
require 'depac-common/constants_cbt'
require 'depac-common/utils'
require 'depac-common/mongoshard'
require 'pp'
require 'yaml'

 

def deployPromotion(envConfig, nodeInfoMap, buildlabel)
  
         if File.exist?(Depac::DeploymentConstants_CBT::CBT_PROMOTION_HOSTMAP_PATH)
             Depac::Log.info("deploymentPromotion_cbt.deployPromotionContainer initial $componentHostMap..............")
             $componentHostMap= YAML.load_file(Depac::DeploymentConstants_CBT::CBT_PROMOTION_HOSTMAP_PATH)
         end
         
        deployPromotionContainer(envConfig, nodeInfoMap, buildlabel)
end


#specComps: specify which component need to be update
#specIPs: specify witch node need to be update
#specNames: specify which Docker container need to be update
def updatePromotion(envConfig, nodeInfoMap, buildlabel, updateConfig)
          if updateConfig !=nil
            if File.exist?(updateConfig)
               updateConfigObj=YAML.load_file(updateConfig)
            end
          end

          #updateConfigObj hash include specComp, specIP, specName)
          updatePromotionContainer(envConfig, nodeInfoMap, buildlabel, updateConfigObj)   
end

def deployPromotionContainer(envConfig, nodeInfoMap, buildlabel)

      if envConfig['enableMongoSharding'] != nil && envConfig['enableMongoSharding'] == true
           if envConfig['mongoShardingModle'] !=nil && envConfig['mongoShardingModle'] == "aloneMongos"            
              Depac::Log.info("deploymentPromotion_cbt.deployPromotionContainer mongo db sharding model enabled!")
              deployPromotionDBSharding(envConfig, nodeInfoMap, buildlabel)          
           elsif envConfig['mongoShardingModle'] !=nil && envConfig['mongoShardingModle'] == "embededMongos"
              Depac::Log.info("deploymentPromotion_cbt.deployPromotionContainer mongo db sharding model enabled!")
              deployPromotionDBSharding_one(envConfig, nodeInfoMap, buildlabel)
           end
      else
           Depac::Log.info("deploymentPromotion_cbt.deployPromotionContainer mongo db will using single model")
           deployPromotionDB(envConfig, nodeInfoMap, buildlabel)
      end             
      deployPromotionApp(envConfig, nodeInfoMap, buildlabel)        
end


def deployPromotionApp(envConfig, nodeInfoMap, buildlabel)
       Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp start..............")
       if envConfig[:vms] != nil && nodeInfoMap !=nil  
            nodeInfoMap.each do |role, nodeArr|
              if role.include?('app')
                
               
                containerArray=Array.new
                cur_roleConfig= getRoleConfigbyRole(envConfig, role)
                componentArray=cur_roleConfig['components'].split(',') if cur_roleConfig['components']
                
                nodeArr.each do |node|
                  
                      node['no_of_inst']=1 unless node['no_of_inst']
                  
                      #Step1: get IP and pushDeployPromotion script to target server 
                      Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp push promotion deploy scripts to server #{node['IP']}..............")
                      if node['IP'] !=nil
                          pushDeploymentScirpt(node['IP'])
                      end  
                    
                      #Step2: SetHost  
                      if node['linkto'] !=nil
                          aggregatedHostMapping=aggregateHost(node['linkto'], envConfig, node['IP'])
                          if node['IP'] !=nil
                                Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp configure hostsmapping #{aggregatedHostMapping} on server #{node['IP']}..............")
                                confgiureHosts(node['IP'], aggregatedHostMapping)
                          end           
                      end 
                      
                      #Step3: remote call scripts to start container
                      if componentArray.size !=0
                          node['containers']=Array.new if node['containers'] == nil
                          componentArray.each do |component|
                            
                              #if no_of_inst ==1, container name don't need add index 
                              if node['no_of_inst'] == 1
                                 Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp start docker container for compoent #{component} with instance name #{buildlabel} on server #{node['IP']}..............")
                                 container=startDockerContainer(component, buildlabel, node['IP'], 0)
                                 node['containers'].push(container)
                              else
                                #if no_of_inst !=1, container name need add index test_1_ruleengine;test_2_ruleengine
                                for i in 1..node['no_of_inst']
                                  Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp start docker container for compoent #{component} with instance name #{buildlabel} on server #{node['IP']}..............")
                                  container=startDockerContainer(component, buildlabel, node['IP'], i)
                                  node['containers'].push(container)
                                end
                              end
                          end
                      end
                end   
              end               
            end
       end
       Depac::Log.info("deploymentPromotion_cbt.deployPromotionSingleApp end..............")   
end


def updatePromotionContainer(envConfig, nodeInfoMap, buildlabel, updateConfig)
        
          #updatePromotionDB(envConfig, nodeInfoMap, buildlabel, updateConfig)
           
          udpatePromotionAPP(envConfig, nodeInfoMap, buildlabel, updateConfig)
end


#identify sequence should be --- IP component dockername
def updatePromotionDB(envConfig, nodeInfoMap, buildlabel, updateConfig)
         if nodeInfoMap !=nil
              nodeInfoMap.each do |role, nodeArr|
                   if role.include?('db')
                         if nodeArr !=nil
                               nodeArr.each do |node|
                                  updatePromtionNode(node, updateConfig, envConfig, role, buildlabel)
                             end
                         end
                   end
              end
         end
end

def udpatePromotionAPP(envConfig, nodeInfoMap, buildlabel, updateConfig)
          if nodeInfoMap !=nil
                nodeInfoMap.each do |role, nodeArr|
                     if role.include?('app')
                           if nodeArr !=nil
                                 nodeArr.each do |node|
                                   updatePromtionNode(node, updateConfig, envConfig, role, buildlabel)  
                                 end  
                           end
                     end
                end
           end
end

def updatePromtionNode(node, updateConfig, envConfig, role, instanName)
       
         hitObject=detectHits(node, updateConfig, envConfig, role)
         Depac::Log.info("deploymentPromotion_cbt.updatePromtionNode hitObject: #{hitObject} for current node #{node['IP']} with role #{role} and instanName #{instanName} ..............")                             
         return if hitObject[node['IP']] == "no_match"
                                          
         return if hitObject['components']==nil
                                         
         return if hitObject['containers']==nil
         
         Depac::Log.info("hitObject['components'] is #{hitObject['components']}..............")                                                   
         hitObject['components'].each do |matchedComps|
                                           
            hitObject['containers'].each do |matchedContainer|
                   if matchedContainer['component'] == matchedComps
                         resetDockerContainer(matchedComps, instanName, matchedContainer['name'], node['IP'])
                   end       
            end
        end
end


def detectHits(node, updateConfig, envConfig, role)
     
      if updateConfig !=nil && node !=nil          
            _hitObject=Hash.new
            
           _hitObject[node['IP']]="no_match"
           if updateConfig['specIPs'] !=nil
               _hitObject[node['IP']]="matched" if updateConfig['specIPs'].include?(node['IP'])  
           else  
               _hitObject[node['IP']]="ignore"        
           end
           
           _hitObject['components']=nil
           if updateConfig['sepcComps'] !=nil
                   containerArray=Array.new
                   matchedCompsArray=Array.new
                   cur_roleConfig= getRoleConfigbyRole(envConfig, role)
                   componentArray=cur_roleConfig['components'].split(',') if cur_roleConfig['components']    
                     
                   componentArray.each do |component|
                      matchedCompsArray.push(component) if updateConfig['sepcComps'].include?(component)
                   end 
                   
                   _hitObject['components']=matchedCompsArray if matchedCompsArray.size !=0
           else 
             _hitObject['components']=componentArray
           end
           
            _hitObject['containers']=nil
           if updateConfig['sepcDockerNames'] !=nil
             
                  matchedContainerArray=Array.new
                  
                  node['containers'].each do |container|
                    matchedContainerArray.push(container)  if updateConfig['sepcDockerNames'].include?(container['name'])
                  end
                  
                  _hitObject['containers']= matchedContainerArray if matchedContainerArray.size !=0                 
           else
               _hitObject['containers']=node['containers']
           end
      end
   
      return _hitObject 
end


def startDockerContainer(component, instanName, ip, port_offset)  
     Depac::Log.info("deploymentPromotion_cbt.startDockerContainer for component #{component} with instance name #{instanName} on server #{ip} with port_offset #{port_offset}..............")   
     containerObj=Hash.new
        
     deployscript=getComponentDeployScirptName(component)     
     
     if component.include?('app') 
            instanName=instanName+"_"+component
            port=getInitPortForComponent(component)
            _port=port.to_i+port_offset.to_i
            port=_port.to_s
            #based using instnanName and port_offset+1 to compose a new insname for each container, in case container name confilite
            if port_offset !=0 
               instanName=instanName+"_"+(port_offset+1).to_s 
               Depac::Log.info("deploymentPromotion_cbt.startDockerContainer start container with insname #{instanName} , port #{port} on server #{ip} by run script #{deployscript}..............")   
            end
     end
     
     
     _containerObj=Hash.new
     _containerObj['component']=component
      
     if deployscript != nil
       
         if component.include?('app')
           deployCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c '/opt/deployscripts/#{deployscript} #{instanName}:#{port} localhost master'\""
         else
           deployCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c '/opt/deployscripts/#{deployscript} #{instanName}'\""
         end
         Depac::Log.info("deploymentPromotion_cbt.startDockerContainer deployCMD launch: #{deployCMD}..............")   
         containerID=`#{deployCMD}`
         
         _instanName=nil
         if component.include?('db') && component.include?('coupon')
            _instanName=instanName+"_coupondb"
         elsif component.include?('db') && component.include?('rule')
            _instanName=instanName+"_rulesdb"
         else
           _instanName=instanName
         end
         
         getContainerIDCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c \"d.lsc | grep #{_instanName} | awk '{print $1}'\"\""
          Depac::Log.info("deploymentPromotion_cbt.startDockerContainer getContainerIDCMD: #{getContainerIDCMD}..............")  
         _containerID=`#{getContainerIDCMD}`
         Depac::Log.info("deploymentPromotion_cbt.startDockerContainer get docker container id #{_containerID}..............")   
         _containerObj['id']=_containerID.chomp
     end
     
     if _containerObj['id'] !=nil
           getNameCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c 'd.name #{_containerObj['id']}'\""
           containerName=`#{getNameCMD}`
           Depac::Log.info("deploymentPromotion_cbt.startDockerContainer get docker container name #{containerName}..............")   
           _containerObj['name']=containerName.chomp
           
         if component.include?('app')
            getPortCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c 'd.hostports #{_containerObj['id']} #{port}'\""
            containerPortsMap=`#{getPortCMD}`
            
            Depac::Log.info("deploymentPromotion_cbt.startDockerContainer get containerPortsMap:  #{containerPortsMap}..............") 
            #if containerPortsMap.length !=0
            if containerPortsMap !=nil
              #need to parse 8080
              #containerPortsMap.each do |record|
                   if containerPortsMap.include?('8080')
                     #map[8080/tcp:[map[HostIp:0.0.0.0 HostPort:9190]]]
                      matchedPorts= /HostPort:([0-9]*)/.match("#{containerPortsMap}")                     
                      _containerPort = matchedPorts.captures                    
                      #break
                   end
              #end
                   _containerObj['port']=_containerPort[0].chomp
                   Depac::Log.info("deploymentPromotion_cbt.startDockerContainer get container port with 8080:  #{_containerObj['port']}..............") 
            else
              _containerObj['port']='8080'
            end
         end     
     end
                
     return _containerObj
end


def resetDockerContainer(component, instanName, dockername, ip)
      resetScript=getComponentReSetScriptName(component)     
      if resetScript != nil
                  if component.include?('app')
                     
                     instanName=instanName+"_"+component
                     resetCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c 'cd /opt/deployscripts/;./#{resetScript} #{instanName} #{dockername}'\""
                  else
                     resetCMD="ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{ip} -o StrictHostKeyChecking=no \"bash --login -c 'cd /opt/deployscripts/;./#{resetScript} #{dockername}'\""
                  end
                  Depac::Log.info("deploymentPromotion_cbt.resetDockerContainer CMD: #{resetCMD}")
                  output=`#{resetCMD}`
                  if $? != 0
                    Depac::Log.info("deploymentPromotion_cbt.resetDockerContainer fail on server #{ip} for #{component} with instname #{instanName} #{output}.........")
                  else
                    Depac::Log.info("deploymentPromotion_cbt.resetDockerContainer success! #{output}........")
                  end
      end
end

def getComponentDeployScirptName(component)
       _scriptName=nil
       
       case component
       
       when 'app-pdsl'
          _scriptName="cd.promotion.pdsl.app.sh"
       when 'app-rule' 
          _scriptName="cd.promotion.rule.app.sh"
       when 'app-calc' 
         _scriptName="cd.promotion.calc.app.sh"
       when 'app-mapper'
         _scriptName=""
       when 'app-coupon'
         _scriptName="cd.coupon.app.sh"
       when 'db-rule'
         _scriptName="cd.promotion.rule.db.sh"
       when 'db-coupon'
         _scriptName="cd.coupon.db.sh"
       else
         Depac::Log.info("deploymentPromotion_cbt.getComponentDeployScirptName: unknow component #{component}!.........")
       end
       
       return _scriptName
end 


def getComponentReSetScriptName(component)
  
         _scriptName=nil
         
         case component
         
         when 'app-mapper'
            _scriptName=""
         when 'app-rule' 
            _scriptName="cd.reset.promotion.app.sh"
         when 'app-pdsl' 
           _scriptName="cd.reset.promotion.app.sh"
         when 'app-calc'
            _scriptName="cd.reset.promotion.app.sh"
         when 'db-rule'
           _scriptName="cd.reset.db.sh"
         when 'db-coupon'
           _scriptName="cd.reset.db.sh"
         when 'app-coupon'
           _scriptName="cd.reset.coupon.app.sh"
         else
           Depac::Log.info("deploymentPromotion_cbt.getComponentReSetScriptName: unknow component #{component}!.........")
         end
         
         return _scriptName    
end

def aggregateHost(linktoArray,envConfig, currentNodeIP)
  
  _aggregatedHost=Hash.new
    
  if linktoArray !=nil
    linktoArray.each do |linkNode|
          _hostname=getPromotionCompHostName(linkNode['component'])
          _ip =nil
         if _hostname !=nil
          if linkNode['ip'] !=nil 
             if linkNode['ip'] == 'self'
               _ip=currentNodeIP
             else         
               _ip=linkNode['ip']
             end
          else     
              if linkNode['index'] ==nil                        
                _ip=searchHostIPInEnvConfig(envConfig, linkNode['component'], 0) 
                  
              else   
                _aggregatedHost[_hostname]=searchHostIPInEnvConfig(envConfig, linkNode['component'], index)
              end
          end
          
          _aggregatedHost[_hostname]=_ip
        end
    end
  end  
  Depac::Log.info("deploymentPromotion_cbt.aggregateHost.result: #{_aggregatedHost}.........")
  return  _aggregatedHost
end


def getIPFromNodeInfoByIndex(nodeInfo, index)
     return nodeInfo[index]['IP'] if nodeInfo[index]
end

def getPromotionCompHostName(component)
      
      if $componentHostMap == nil
         return nil
      end
      
      return  $componentHostMap[component]
end

def getInitPortForComponent(component)
      case component
        
      when "app-rule"
        return  9292
      when "app-pdsl"
        return  9191
      when "app-calc"
        return  9393
      when "app-coupon"
        return  9494
      else
        Depac::Log.info("deploymentPromotion_cbt.getInitPortForComponent.unknow component: #{component}.........")
      end
end

def getRoleConfigbyRole(envConfig, role)
       if envConfig[:vms] !=nil
            envConfig[:vms].each do |roleConfig|
                 if roleConfig['role'] == role
                       return roleConfig
                 end
            end
       end       
       return nil
end


# This methode used to push DeploymentScirpts to target server
def pushDeploymentScirpt(targetIP)
  
  #copy package to tmp directory
  uploadCMD = "scp -o StrictHostKeyChecking=no -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} #{Depac::DeploymentConstants_CBT::CBT_DEPLOY_ROOT_PATH}/#{Depac::DeploymentConstants_CBT::CBT_PROMOTION_DEPLOY_PACKAGE} root@#{targetIP}:/tmp"
  Depac::Log.info("deploymentPromotion_cbt.pushDeploymentScirpt.uploadCMD: #{uploadCMD}.........")
  if Depac::Utils.logged_remoteExec(uploadCMD, "deploymentPromotion_cbt.pushDeploymentScirpt.upload: ") then Depac::Log.fatal_exit("Error while upload promotion deploy scripts to server #{targetIP}......"); end

  #unzip package to webapps
  unzipCMD = "ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{targetIP} -o StrictHostKeyChecking=no 'cd /tmp;rm -rf #{Depac::DeploymentConstants_CBT::CBT_DOCKER_DEPLOY_INSTALL_PARENT_PATH}/deployscripts;unzip -oq #{Depac::DeploymentConstants_CBT::CBT_PROMOTION_DEPLOY_PACKAGE} -d #{Depac::DeploymentConstants_CBT::CBT_DOCKER_DEPLOY_INSTALL_PARENT_PATH};chmod -R 755 #{Depac::DeploymentConstants_CBT::CBT_DOCKER_DEPLOY_INSTALL_PARENT_PATH}/deployscripts/*'"
  Depac::Log.info("deploymentPromotion_cbt.pushDeploymentScirpt.unzipCMD: #{unzipCMD}.........")
  if Depac::Utils.logged_remoteExec(unzipCMD, "deploymentPromotion_cbt.pushDeploymentScirpt.unzip: ") then Depac::Log.fatal_exit("Error while unzip deploy package to server #{targetIP} fail......"); end  

end


def confgiureHosts(targetIP, hostIPHash)
    
    hostIPParams=nil      
    #hostname1:IP hostname2:IP hostname3:IP
    hostIPHash.each do |key, value|     
        value='127.0.0.1' unless value
          
        if hostIPParams == nil         
           hostIPParams=key+":"+value
        else
           hostIPParams=hostIPParams+" "+key+":"+value
        end 
    end
    
    Depac::Log.info("deployPromotion_cbt.configureHosts.hostIPParams is: #{hostIPParams}......")   
    configHostCMD = "ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{targetIP} -o StrictHostKeyChecking=no 'cd /scripts;./configureHosts.sh #{hostIPParams}'"
    Depac::Log.info("deployPromotion_cbt.configureHosts.configHostCMD: #{configHostCMD}.........")
    if Depac::Utils.logged_remoteExec(configHostCMD, "deployPromotion_cbt.configureHosts.configHostCMD: ") then  Depac::Log.fatal_exit("Error while configure hosts mapping on server #{targetIP} fail......"); end  
end

def searchHostIPInEnvConfig(envConfig, component, index)
       _ip=nil
       if envConfig[:vms] !=nil
         envConfig[:vms].each do |roleConfig|
             if roleConfig['components'] !=nil && roleConfig['components'].include?(component) &&roleConfig['node_info'] !=nil        
                 roleConfig['node_info'].each do |node|               
                    if node['reversproxy'] != nil                  
                       _ip= node['reversproxy']
                       break                       
                    else
                      if index !=nil                         
                         _ip=getIPFromNodeInfoByIndex(roleConfig['node_info'], index)
                      end                     
                    end
                 end
             end
        end  
      end   
       
      return _ip
end

#just for mongo sharding the mongos and mongoconf will deploy with app node together
def deployPromotionDBSharding_one(envConfig, nodeInfoMap, buildlabel)
  Depac::Log.info("deploymentPromotion_cbt.deployPromotionDBSharding_one start..............")
  if envConfig!=nil && envConfig[:vms] !=nil
          envConfig[:vms].each do |roleConfig|
                   if roleConfig['role'].include?('db-mongos-shadow')
                     if roleConfig['components'] !=nil
                                                      i=0
                                                      roleConfig['components'].split(',').each do |component|
                                                            i=i+1
                                                            mongsAgentIPs=getIPbylinkComponent(component, nodeInfoMap)  
                                                             

                                                            if roleConfig['shardconfig'] !=nil && mongsAgentIPs!=nil
                                                                                      
                                                                 roleConfig['shardconfig'].each do |mongoshard|
                                                                       
                                                                                           
                                                                              if mongoshard['component'] == component
                                                                                   Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding_one: start handle mongo sharding for component #{component}!......")   
                                                                                
                                                                                   specPort=nil
                                                                                     
                                                                                   case component                                                              
                                                                                   when 'db-coupon'
                                                                                     specPort="27017"
                                                                                   when 'db-rule'
                                                                                     specPort="27017"
                                                                                   end  
                                                                                   
                                                                                   Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding_one: current specPort is  #{specPort}......")                                                   
                                                                                
                                                                                   shardTargetNodes=nil
                                                                                
                                                                                   if mongoshard['shardsIPs'] !=nil
                                                                                         shardTargetNodes=mongoshard['shardsIPs'].split(',')
                                                                                         
                                                                                   elsif mongoshard['shardsNodes'] !=nil
                          
                                                                                         shardTargetNodes=Depac::Utils.getIPListbyRole(nodeInfoMap, mongoshard['shardsNodes'])
                                                                                   end
                                                                                   
                                                                                   _specPort=((specPort.to_i)+i).to_s
                                                                                   Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding_one: current _specPort for shard node is  #{_specPort}......")
                                                                                   shardTargetNodes=addPortWithIP(shardTargetNodes, _specPort)
                                                                                   Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding_one: shardTargetNodes is  #{shardTargetNodes}......")
                                                                                   
                                                                                   replicset=MongoShards::ReplicSet.new("#{buildlabel}.#{component}.shard")
                                                                                                                                                                                                                                                    
                                                                                   
                                                                                   shards=Array.new
                                                                                   #start shard db on shard target node
                                                                                   shardTargetNodes.split(",").each_with_index do |hostport,i|
                                                                                     
                                                                                              shard=MongoShards::MongoShard.new "#{replicset.name}#{i}",hostport,replicset.name
                                                                                              
                                                                                              #if add new app node, don't need to be execute
                                                                                              #=========================================================
                                                                                              if mongoshard['shardsExist'] == nil || mongoshard['shardsExist'] == false
                                                                                                     shard.create
                                                                                              end
                                                                                              #=========================================================
                                                                                              
                                                                                              shards.push(shard)
                                                                                   end
                                                                                   
                                                                                   replicset.shards=shards
                                                                                   
                                                                                   
                                                                                   #if add new app node, don't need to be execute
                                                                                   #=========================================================
                                                                                   if mongoshard['shardsExist'] == nil || mongoshard['shardsExist'] == false
                                                                                       replicset.cleanlocaldb
                                                                                       replicset.create
                                                                                   end
                                                                                   #=========================================================
                                                                                   
                                                                                   ##############################################################
                                                                                   if component=="db-rule"
                                                                                          mongoConfHostPort=(shards.first).host+":"+(specPort.to_i+1000).to_s
                                                                                   else
                                                                                          mongoConfHostPort=(shards.first).host+":"+(specPort.to_i+1500).to_s
                                                                                   end
                                                                                   
                                                                                   mongconf=MongoShards::MongoConf.new("#{buildlabel}.#{component}.mongoconf1","#{mongoConfHostPort}")
                                                                                   
                                                                                   #if add new app node, don't need to be execute
                                                                                   #========================================================= 
                                                                                   if mongoshard['shardsExist'] == nil || mongoshard['shardsExist'] == false                                                                             
                                                                                          mongconf.create.wait4Connected do |_conf|
                                                                                               sleep 5
                                                                                               Depac::Log.info("waiting 5s for conf connection")   
                                                                                          end
                                                                                   end
                                                                                   #=========================================================
                                                                                   
                                                                                   ##############################################################
                                                                                   
                                                                                   if mongsAgentIPs !=nil
                                                                                        mongsAgentIPs.each do |mongosIP|
                                                                                    
                                                                                            mongos=MongoShards::MongoS.new("#{buildlabel}.#{component}.mongos","#{mongosIP}:#{specPort}")
                                                                                            #if component=="db-rule"
                                                                                            #  mongoConfHostPort=mongosIP+":"+(specPort.to_i+1000).to_s
                                                                                            #else
                                                                                            #  mongoConfHostPort=mongosIP+":"+(specPort.to_i+1500).to_s
                                                                                            #end
                                                                                            
                                                                                            #mongconf=MongoShards::MongoConf.new("#{buildlabel}.#{component}.mongoconf1","#{mongoConfHostPort}")
                                                                                   
                                                                                            #mongconf.create.wait4Connected do |_conf|
                                                                                            #    sleep 5
                                                                                            #     Depac::Log.info("waiting 5s for conf connection")   
                                                                                            #end
                                                                                   
                                                                                            mongos.addConf(mongconf);
                                                                                            mongos.create
                                                                                            mongos.addReplicSet replicset
                                                                                        end
                                                                                   end
                                                                              end
                                                                      end
                                                             end   
                                                            
                                                       end
                                                  end

                   end       
          end  
  end
  
  Depac::Log.info("deploymentPromotion_cbt.deployPromotionDB_one end..............")
end

#just for mongo sharding, the mongos and mongoconf will be a individual node
def deployPromotionDBSharding(envConfig, nodeInfoMap, buildlabel)
         Depac::Log.info("deploymentPromotion_cbt.deployPromotionDBSharding start..............")
         if nodeInfoMap !=nil
               nodeInfoMap.each do |role, nodeArr|
                    if role.include?('db')  
                      Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding: detect db role #{role}, will handle it!......")                     
                       
                      nodeArr.each do |dbnode|
                           #1. puts deployscripts to target server
                           pushDeploymentScirpt(dbnode['IP'])
                           
                           
                           if dbnode['components'] !=nil
                             
                                dbnode['components'].split(',').each do |component|
                                     if dbnode['shardconfig'] !=nil
                                                 
                                                         
                                              dbnode['shardconfig'].each do |mongoshard|
                                                       
                                                                    
                                                       if mongoshard['component'] == component
                                                            Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding: start handle mongo sharding for component #{component}! on server #{dbnode['IP']}......")   
                                                         
                                                            specPort=nil
                                                              
                                                            case component                                                              
                                                            when 'db-coupon'
                                                              specPort="27017"
                                                            when 'db-rule'
                                                              specPort="27017"
                                                            end  
                                                            
                                                            Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding: current specPort is  #{specPort}......")                                                   
                                                         
                                                            shardTargetNodes=nil
                                                         
                                                            if mongoshard['shardsIPs'] !=nil
                                                                  shardTargetNodes=mongoshard['shardsIPs'].split(',')
                                                                  
                                                            elsif mongoshard['shardsNodes'] !=nil
   
                                                                  shardTargetNodes=Depac::Utils.getIPListbyRole(nodeInfoMap, mongoshard['shardsNodes'])
                                                            end
                                                              
                                                            shardTargetNodes=addPortWithIP(shardTargetNodes, specPort)
                                                            Depac::Log.info("deployPromotion_cbt.deployPromotionDBSharding: shardTargetNodes is  #{shardTargetNodes}......")
                                                            
                                                            replicset=MongoShards::ReplicSet.new("#{buildlabel}.#{component}.shard")
                                                            mongos=MongoShards::MongoS.new("#{buildlabel}.#{component}.mongos","#{dbnode['IP']}:#{specPort}")
                                                            
                                                            shards=Array.new
                                                            #start shard db on shard target node
                                                            shardTargetNodes.split(",").each_with_index do |hostport,i|
                                                              
                                                                       shard=MongoShards::MongoShard.new "#{replicset.name}#{i}",hostport,replicset.name
                                                                       shard.create
                                                                       shards.push(shard)
                                                            end
                                                            
                                                            replicset.shards=shards
                                                            
                                                            replicset.cleanlocaldb
                                                            
                                                            replicset.create
                                                            
                                                            mongoConfHostPort=dbnode['IP']+":"+(specPort.to_i+1000).to_s
                                                            mongconf=MongoShards::MongoConf.new("#{buildlabel}.#{component}.mongoconf1","#{mongoConfHostPort}")
                                                            
                                                            mongconf.create.wait4Connected do |_conf| 
                                                                      sleep 5
                                                                      Depac::Log.info("waiting 5s for conf connection")   
                                                            end
                                                            
                                                            mongos.addConf(mongconf);
                                                            mongos.create
                                                            mongos.addReplicSet replicset
                                                            
                                                       end
                                               end
                                      end   
                                     
                                end
                           end
                          
                      end                  
                    end
               end
         end
         
         
   #MongoS.new("$instance.mongos","#{host}"){|mongos|
   #  replicset=ReplicSet.new("$instance.shard"){|rset|
   #      shards=[];
   #      "$targethosts".split(",").each_with_index{|hostport,i|
   #                          shard=MongoShard.new "#{rset.name}#{i}",hostport,rset.name;
   #                          shard.create;
   #                          shards.push shard;
   #                                    };
   #      shards
   #  };
   # replicset.cleanlocaldb;
   # replicset.create;
   # mongconf=MongoConf.new("$instance.mongoconf1","#{host}");
   # mongconf.create.wait4Connected{|_conf| sleep 3;log.debug "waiting for conf connection";};
   # mongos.addConf(mongconf);
   # mongos.create;
   # mongos.addReplicSet replicset;
         
         
   Depac::Log.info("deploymentPromotion_cbt.deployPromotionDB end..............")
end

def getIPbylinkComponent(component, nodeInfoMap)
  
        ipArr=Array.new
        
        if nodeInfoMap !=nil
            nodeInfoMap.each do |role, nodeArr|
                  nodeArr.each do |node|
                       if node['linkto'] !=nil                        
                            node['linkto'].each do |linkedNode|                             
                                    if linkedNode['component'] == component                                     
                                        ipArr.push(node['IP'])   
                                    end
                            end                                                     
                       end                 
                  end           
            end       
        end
        
        Depac::Log.info("deploymentPromotion_cbt.getIPbyComponent with component #{component} : #{ipArr}..............")
        
        return ipArr
end

def addPortWithIP(ips, port)
   
      hostPorts=nil 
      
      if ips and port
        i = 0
        ips.each do |ip|
            if hostPorts == nil       
               hostPorts=ip.to_s+":"+port.to_s
            else
               hostPorts=hostPorts+","+ip.to_s+":"+port.to_s
            end   
            i=i+1
         end
        
      end
      
      return hostPorts
end


def deployPromotionDB(envConfig, nodeInfoMap, buildlabel)
       Depac::Log.info("deploymentPromotion_cbt.deployPromotionDB start..............")
       if nodeInfoMap !=nil
             nodeInfoMap.each do |role, nodeArr|
                  if role.include?('db')  
                    Depac::Log.info("deployPromotion_cbt.deployPromotionDB: detect db role #{role}, will handle it!......")                     
                     
                    nodeArr.each do |dbnode|
                         #1. puts deployscripts to target server
                         pushDeploymentScirpt(dbnode['IP'])
                         
                         if dbnode['no_of_inst']==nil
                            dbnode['no_of_inst']=1
                         end
                           
                         
                         for i in 1..dbnode['no_of_inst']                                
                             #2. remote call script to start db container
                             _component= role
                             
                             dbnode['containers']=Array.new if dbnode['containers'] == nil
                                                        
                             componentsArray=dbnode['components'].split(',')
                                  
                             componentsArray.each do |component|
                                        Depac::Log.info("deployPromotion_cbt.deployPromotionDB: start container of #{component} with instname #{buildlabel} on server #{dbnode['IP']}......")
                                        container=startDockerContainer(component, buildlabel, dbnode['IP'], 0)  
                                        Depac::Log.info("deployPromotion_cbt.deployPromotionDB: get response docker info #{container}......")
                                        dbnode['containers'].push(container)
                             end

                         end

                    end                  
                  end
             end
       end
       Depac::Log.info("deploymentPromotion_cbt.deployPromotionDB end..............")
end
